package com.request.service.impl.shop.yicheng;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.request.common.constants.RedisConstant;
import com.request.common.enums.OperatorChannelTypeEnum;
import com.request.common.enums.SignTypeEnum;
import com.request.common.enums.YiChengErrorCodeEnum;
import com.request.common.enums.YiChengGoodsIdEnum;
import com.request.common.utils.HttpUtils;
import com.request.common.utils.MoneyUtils;
import com.request.common.utils.SignUtils;
import com.request.entity.PhoneOrderAvailable;
import com.request.entity.PhoneOrderWaitPush;
import com.request.entity.out.PushOrderForYiCheng;
import com.request.service.PushOrderProcessor;
import com.request.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Description:
 * Create DateTime: 2020-04-22 16:09
 *
 * 

 */
@Slf4j
@Service
public class YiChengPushOrderProcessor implements PushOrderProcessor {

    private static Map<String, String> HEADER_MAP = new HashMap<>(2);

    static {
        HEADER_MAP.put("Accept", "application/json;charset=utf-8");
    }

    @Resource
    private RedisService redisService;
    @Resource
    private ThreadPoolExecutor handlerOrderExecutor;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public void sendRequest(PhoneOrderWaitPush phoneOrderWaitPush) {
        handlerOrderExecutor.execute(() -> executeSend(phoneOrderWaitPush));
    }

    private void executeSend(PhoneOrderWaitPush phoneOrderWaitPush) {
        if (phoneOrderWaitPush == null) {
            return;
        }

        PhoneOrderAvailable phoneOrderAvailable = phoneOrderWaitPush.getPhoneOrderAvailable();
        PushOrderForYiCheng pushOrderForYiCheng = new PushOrderForYiCheng();
        String result = null;
        try {
            String type = phoneOrderAvailable.getType();

            OperatorChannelTypeEnum byCode = OperatorChannelTypeEnum.getByCode(type);

            YiChengGoodsIdEnum goodsIdEnum = YiChengGoodsIdEnum.getByPhoneOperatorAndPrice(phoneOrderAvailable.getPhoneOperator(),
                    phoneOrderAvailable.getOrderPrice().setScale(0).toPlainString(), isElse(byCode));

            Date date = new Date();

            //组装参数
            pushOrderForYiCheng.setUserId(phoneOrderWaitPush.getAppId());
            pushOrderForYiCheng.setItemId(goodsIdEnum.getGoodsId());
            pushOrderForYiCheng.setCheckItemFacePrice(String.valueOf(MoneyUtils.getCiFromAmout(phoneOrderAvailable.getOrderPrice())));
            pushOrderForYiCheng.setUid(phoneOrderAvailable.getPhoneNum());
            pushOrderForYiCheng.setSerialno(phoneOrderAvailable.getPlatformOrderNo());
            pushOrderForYiCheng.setDtCreate(DateUtil.format(date, DatePattern.PURE_DATETIME_PATTERN));

            //生成签名
            TreeMap<String, String> signMap = new TreeMap<>();
            signMap.put("a", pushOrderForYiCheng.getCheckItemFacePrice());
            signMap.put("b", pushOrderForYiCheng.getDtCreate());
            signMap.put("c", pushOrderForYiCheng.getItemId());
            signMap.put("d", pushOrderForYiCheng.getSerialno());
            signMap.put("e", pushOrderForYiCheng.getUid());
            signMap.put("f", pushOrderForYiCheng.getUserId());
            signMap.put("g", phoneOrderWaitPush.getAppKey());

            String sign = SignUtils.generateSignature(signMap, SignTypeEnum.MD5);
            pushOrderForYiCheng.setSign(sign);

            try {
                result = sendRequestForPushOrder(phoneOrderWaitPush.getPushSite(), pushOrderForYiCheng);
            } catch (Exception e) {
                savePushOrderForException(phoneOrderWaitPush);
                log.info("【翼程】【推单】发送请求出现异常：", e);
                return;
            }

            JSONObject json = JSONObject.parseObject(result);
            String code = json.getString("code");

            if (!YiChengErrorCodeEnum.SUCCESS.getCode().equals(code)) {
                log.info("【翼程】【推单】响应状态失败,状态为：{}", code);
                savePushOrderForFailed(phoneOrderWaitPush);
                return;
            }
            savePushOrderForSuccess(phoneOrderAvailable.getPlatformOrderNo());
        } catch (Exception e) {
            savePushOrderForSuccess(phoneOrderAvailable.getPlatformOrderNo());
            log.error("【翼程】【推送订单】推送订单失败，待推送订单信息为：{}, 请求参数为：{}, 商户响应结果为：{}, 详细错误信息为：",
                    phoneOrderWaitPush, pushOrderForYiCheng, result, e);
        }
    }

    /**
     * 发送Http请求
     *
     * @param url                 请求路径
     * @param pushOrderForYiCheng 参数
     * @return 响应结果
     */
    private String sendRequestForPushOrder(String url, PushOrderForYiCheng pushOrderForYiCheng) {
        Map<String, Object> paramMap = BeanUtil.beanToMap(pushOrderForYiCheng);
        return HttpUtils.doPost(url, paramMap, HEADER_MAP);
    }

    /**
     * 放入失败队列
     */
    private void savePushOrderForFailed(PhoneOrderWaitPush phoneOrderWaitPush) {
        redisService.lPush(RedisConstant.PUSH_ORDER_WAIT_PUSH_RECORD, JSON.toJSONString(phoneOrderWaitPush));
    }

    /**
     * 放入成功队列
     */
    private void savePushOrderForSuccess(String platformOrderNo) {
        redisTemplate.opsForValue().set(RedisConstant.PUSH_ORDER_WAIT_PUSH_SUCCESS + platformOrderNo, platformOrderNo, 30, TimeUnit.MINUTES);
    }

    /**
     * 放入发送出现异常的队列
     */
    private void savePushOrderForException(PhoneOrderWaitPush phoneOrderWaitPush) {
        redisService.lPush(RedisConstant.PUSH_ORDER_WAIT_PUSH_EXCEPTION, JSON.toJSONString(phoneOrderWaitPush));
    }

    private Boolean isElse(OperatorChannelTypeEnum typeEnum) {
        return OperatorChannelTypeEnum.WO.getCode().equals(typeEnum.getCode())
                || OperatorChannelTypeEnum.YI.getCode().equals(typeEnum.getCode());
    }
}
